package top.alpacaknight.compressedfilegenerator;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;

/**
 * Created by alpacaknight on 17-4-27.
 * Java文件压缩/解压工具.
 */

public class ZipUtils {

    /**
     * 压缩多个指定的文件或文件夹到指定的压缩文件
     * Compress the specified files or folders and store them to a specified file.
     * @param filePathList: 将要压缩的文件/文件夹的完整路径, the path of file or folder which will be compressed
     * @param zipPath: 生成的压缩文件的完整路径,The full path of compressed file which will be generated
     */
    public static void zipFileByPathList(ArrayList<String> filePathList, String zipPath) {
        try {
            FileOutputStream zipFile = new FileOutputStream(zipPath);
            BufferedOutputStream buffer = new BufferedOutputStream(zipFile);
            ZipOutputStream outStream = new ZipOutputStream(buffer);
            for (String srcFiles : filePathList){
                zipFiles(srcFiles, outStream, "");
            }
            outStream.close();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    /**
     * 压缩指定的文件或文件夹到指定的压缩文件
     * Compress the specified file or folder and store them to a specified file.
     * @param filepath: 将要压缩的文件/文件夹的完整路径, the path of file or folder which will be compressed
     * @param zipPath: 生成的压缩文件的完整路径,The full path of compressed file which will be generated
     */
    public static void zipFilesByPath(String filepath, String zipPath) {
        try {
            FileOutputStream zipFile = new FileOutputStream(zipPath);
            BufferedOutputStream buffer = new BufferedOutputStream(zipFile);
            ZipOutputStream outStream = new ZipOutputStream(buffer);
            zipFiles(filepath, outStream, "");
            outStream.close();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    /**
     * Recursively add all files in the specified path.递归添加指定路径下所有的文件.
     * @param filePath: compress file.压缩文件.
     * @param outStream: the .zip file's outputStream.压缩文件的输出流
     * @param prefix: the parent folder name of the file that makes the tier relation.目录路径前缀,用于表示目录结构
     */
    public static void zipFiles(String filePath, ZipOutputStream outStream, String prefix) throws IOException {
        File file = new File(filePath);
        if (file.isDirectory()) {
            //文件夹的处理,判断并递归处理所有包含的子项
            if (file.listFiles().length == 0) {
                //空目录
                ZipEntry zipEntry = new ZipEntry(prefix + file.getName() + "/");
                outStream.putNextEntry(zipEntry);
                outStream.closeEntry();
            } else {
                //非空目录的递归处理
                prefix += file.getName() + File.separator;
                for (File f : file.listFiles()){
                    zipFiles(f.getAbsolutePath(), outStream, prefix);
                }
            }
        } else {
            FileInputStream inStream = new FileInputStream(file);
            ZipEntry zipEntry = new ZipEntry(prefix + file.getName());
            outStream.putNextEntry(zipEntry);
            byte[] buf = new byte[1024];
            int len;
            while ((len = inStream.read(buf)) > 0) {
                outStream.write(buf, 0, len);
            }
            outStream.closeEntry();
            inStream.close();
        }

    }

    /**
     * 解压指定的文件到指定目录
     * Unzip the file to the specified path.
     * @param filename: 被解压的文件完整路径, The full path of the extracted file.
     * @param prefix: 解压的根目录, the prefix is the root of the store path.
     * @IOExcetion: the ioexception during unzipFiles.
     */
    public static void unzipFileToPath(String filename, String prefix) throws IOException {
        if (filename == null || filename.equals("")) {
            throw new NullPointerException("File is not exist!");
        }

        InputStream inputStream = new FileInputStream(filename);
        ZipInputStream zipInputStream = new ZipInputStream(inputStream);
        ZipEntry zipEntry;
        while ((zipEntry = zipInputStream.getNextEntry()) != null) {
            if (zipEntry.isDirectory()) {
                File file = new File(prefix + zipEntry.getName());
                if (!file.exists())
                    file.mkdirs();
                continue;
            }
            File file = new File(prefix + zipEntry.getName());
            if (!file.getParentFile().exists())
                file.getParentFile().mkdirs();
            ByteArrayOutputStream toScan = new ByteArrayOutputStream();
            byte[] buf = new byte[1024];
            int len;
            while ((len = zipInputStream.read(buf)) > 0) {
                toScan.write(buf, 0, len);
            }
            byte[] fileOut = toScan.toByteArray();
            toScan.close();
            //writeByteFile(fileOut, new File(prefix + zipEntry.getName()));

            FileOutputStream outputStream = new FileOutputStream(new File(prefix + zipEntry.getName()));
            outputStream.write(fileOut);
            outputStream.close();

        }
        zipInputStream.close();
        inputStream.close();
    }

    /**
     * 解压文件
     * Unzip the file.
     * @param bytes: the content of the zipfile by byte array.     *
     * @param prefix: the prefix is the root of the store path.
     * @IOExcetion: the ioexception during unzipFiles.
     */
    public static void unzipFilesWithTier(byte[] bytes, String prefix) throws IOException {

        InputStream bais = new ByteArrayInputStream(bytes);
        ZipInputStream zin = new ZipInputStream(bais);
        ZipEntry ze;
        while ((ze = zin.getNextEntry()) != null) {
            if (ze.isDirectory()) {
                File file = new File(prefix + ze.getName());
                if (!file.exists())
                    file.mkdirs();
                continue;
            }
            File file = new File(prefix + ze.getName());
            if (!file.getParentFile().exists())
                file.getParentFile().mkdirs();
            ByteArrayOutputStream toScan = new ByteArrayOutputStream();
            byte[] buf = new byte[1024];
            int len;
            while ((len = zin.read(buf)) > 0) {
                toScan.write(buf, 0, len);
            }
            byte[] fileOut = toScan.toByteArray();
            toScan.close();
            writeByteFile(fileOut, new File(prefix + ze.getName()));
        }
        zin.close();
        bais.close();
    }

    public static byte[] readFileByte(String filename) throws IOException {

        if (filename == null || filename.equals("")) {
            throw new NullPointerException("File is not exist!");
        }
        File file = new File(filename);
        long len = file.length();
        byte[] bytes = new byte[(int) len];

        BufferedInputStream bufferedInputStream = new BufferedInputStream(
                new FileInputStream(file));
        int r = bufferedInputStream.read(bytes);
        if (r != len)
            throw new IOException("Read file failure!");
        bufferedInputStream.close();

        return bytes;

    }

    public static String writeByteFile(byte[] bytes, File file) {
        FileOutputStream fos = null;
        try {
            fos = new FileOutputStream(file);
            fos.write(bytes);
        } catch (FileNotFoundException e) {
            e.printStackTrace();

        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (fos != null) {
                try {
                    fos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return "success";
    }
}
